#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pthread.h>

void usage()
{
    puts("Usage:");
    puts(" pgrep string path");
    puts(" pgrep -r string path");
    exit(0);
}

#define CAPACITY 100

char buffer[CAPACITY][300];
int in,out;
char *target;

int buffer_is_empty()
{
    return in == out;
}

int buffer_is_full()
{
    return (in + 1) % CAPACITY == out;
}

char *get_path()
{
    char *path;
    path = buffer[out];
    out = (out + 1) % CAPACITY;
    return path;
}

void put_path(char *path)
{
    strcpy(buffer[in], path);
    in = (in + 1) % CAPACITY;
}

pthread_mutex_t mutex;
pthread_cond_t wait_empty_buffer, wait_full_buffer;

void grep_dir(char *path)
{
    DIR *dir = opendir(path);
    struct dirent *entry;
    while (entry = readdir(dir)) {
        if (strcmp(entry->d_name, ".") == 0)
            continue;
        if (strcmp(entry->d_name, "..") == 0)
            continue;
        char sub_path[300];
        sprintf(sub_path, "%s/%s", path, entry->d_name);
        if (entry->d_type == DT_DIR)
            grep_dir(sub_path);
        if (entry->d_type == DT_REG) {
            pthread_mutex_lock(&mutex);
            while (buffer_is_full())
                pthread_cond_wait(&wait_empty_buffer, &mutex);
                put_path(sub_path);
                pthread_cond_signal(&wait_full_buffer);
                pthread_mutex_unlock(&mutex);
        }
    }
}

void grep_file(char *path)
{
    FILE *file = fopen(path, "r");
    char line[256];
    while (fgets(line, sizeof(line), file)) {
        if (strstr(line, target))
            printf("%s:%s", path, line);
    }
    fclose(file);
}

void *consume(void *arg)
{
    while (1) {
        pthread_mutex_lock(&mutex);
        while (buffer_is_empty())
            pthread_cond_wait(&wait_full_buffer, &mutex);
        char *string = get_path();
        pthread_cond_signal(&wait_empty_buffer);
        pthread_mutex_unlock(&mutex);
        if (string[0] == 0)
            pthread_exit(NULL);
        grep_file(string);
    }
}

int main(int argc, char *argv[])
{
    if (argc < 3)
        usage();

    char *target = argv[argc-2];
    char *path = argv[argc-1];
    if (strcmp(argv[1], "-r") == 0) {
        pthread_t consumer_tid1, consumer_tid2;
        pthread_mutex_init(&mutex, NULL);
        pthread_cond_init(&wait_empty_buffer, NULL);
        pthread_cond_init(&wait_full_buffer, NULL);
        pthread_create(&consumer_tid1, NULL, consume, NULL);
        pthread_create(&consumer_tid2, NULL, consume, NULL);
        grep_dir(path);
        pthread_mutex_lock(&mutex);
        while (buffer_is_full())
            pthread_cond_wait(&wait_empty_buffer, &mutex);
        buffer[in][0] = 0;
        in = (in + 1) % CAPACITY;
        pthread_cond_signal(&wait_full_buffer);
        pthread_mutex_unlock(&mutex);

        pthread_mutex_lock(&mutex);
        while (buffer_is_full())
            pthread_cond_wait(&wait_empty_buffer, &mutex);
        buffer[in][0] = 0;
        in = (in + 1) % CAPACITY;
        pthread_cond_signal(&wait_full_buffer);
        pthread_mutex_unlock(&mutex);

        pthread_join(consumer_tid1, NULL);
        pthread_join(consumer_tid2, NULL);
    }
    else
        grep_file(path);
    return 0;
}
